package bb.lanxing.common.engin.util;

import android.location.Location;
import android.location.LocationListener;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;

import com.github.mikephil.charting.utils.Utils;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.LinkedList;

public class LocationSimulator {
    private static final String TAG = "LocSimulator";

    private static LocationSimulator instance = null;
    public static boolean isLocSimulatorOpen = false;

    private Location curLocation;
    private LocationListener locationListener;
    private RandomAccessFile mReader;
    private long timeDifference;

    private Handler handler = new Handler(Looper.getMainLooper());
    private LinkedList<Location> locations = new LinkedList<>();

    private LocationSimulator() {
        try {
            mReader = new RandomAccessFile(EnginUtil.getGpsSimulateFile(), "r");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static LocationSimulator getInstance() {
        if (instance == null) {
            instance = new LocationSimulator();
        }
        return instance;
    }

    public boolean registerLocationListener(LocationListener locationListener) throws IOException {
        if (locationListener == null || this.locationListener == locationListener) {
            return false;
        }
        this.locationListener = locationListener;
        startSimulator();
        return true;
    }

    public void unregisterLocationListener() {
        locationListener = null;
        locations.clear();
        handler.removeCallbacksAndMessages(null);
    }

    private void startSimulator() throws IOException {
        if (!locations.isEmpty()) {
            return;
        }
        try {
            mReader.seek(0L);
        } catch (IOException e) {
            e.printStackTrace();
        }
        cacheLocations();
        if (locations.isEmpty()) {
            return;
        }
        curLocation = locations.peek();
        timeDifference = System.currentTimeMillis() - curLocation.getTime();
        nextLocation();
    }

    private void nextLocation() throws IOException {
        if (locations.size() <= 1) {
            cacheLocations();
        }
        if (locations.isEmpty()) {
            return;
        }
        final Location poll = locations.poll();
        handler.postDelayed(() -> {
            curLocation.setTime(curLocation.getTime() + timeDifference);
            if (locationListener != null) {
                locationListener.onLocationChanged(curLocation);
            }
            Log.i(LocationSimulator.TAG, "loc: " + poll.getLatitude() + "," + poll.getLongitude());
            curLocation = poll;
            try {
                nextLocation();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }, (poll.getTime() - curLocation.getTime()) / 2);
    }

    private void cacheLocations() throws IOException {
        String readLine;
        while (locations.size() < 50 && (readLine = mReader.readLine()) != null) {
            locations.add(decode(readLine));
        }
    }

    private Location decode(String str) {
        String[] split = str.split(",");
        Location location = new Location(split[7]);
        location.setTime(toLong(split[0]));
        location.setLatitude(toDouble(split[1]));
        location.setLongitude(toDouble(split[2]));
        location.setAltitude(toDouble(split[3]));
        location.setAccuracy(toFloat(split[4]));
        location.setSpeed(toFloat(split[5]));
        location.setBearing(toFloat(split[6]));
        if (Build.VERSION.SDK_INT >= 17) {
            location.setElapsedRealtimeNanos(toLong(split[8]));
        }
        return location;
    }

    private Long toLong(String str) {
        if (TextUtils.isEmpty(str)) {
            return 0L;
        }
        return Long.parseLong(str);
    }

    private Float toFloat(String str) {
        if (TextUtils.isEmpty(str)) {
            return 0.0f;
        }
        return Float.parseFloat(str);
    }

    private Double toDouble(String str) {
        if (TextUtils.isEmpty(str)) {
            return Utils.DOUBLE_EPSILON;
        }
        return Double.parseDouble(str);
    }
}